import copy
import io
import json
import logging

from PIL import Image
import numpy as np
from nicegui import ui, events
from nicegui.events import GenericEventArguments, ValueChangeEventArguments
from starlette.formparsers import MultiPartParser

from opencv_algo_wrapper import OpencvAlgoWrapper

MultiPartParser.max_file_size = 1024 * 1024 * 10  # 5 MB

columns = [{'field': 'id'},
           {'field': '算法', 'editable': True},
           {'field': '参数配置', 'editable': True, 'sortable': True},
           ]
rows = [
    {'id': 0, 'name': 'Alice', 'age': 18},
    {'id': 1, 'name': 'Bob', 'age': 21},
    {'id': 2, 'name': 'Carol', 'age': 20},
]


class OpencvPositioningFrame:
    def __init__(self, logger: logging.Logger):
        self.aggrid = None
        self.json_editor = None
        self.main_ui = None
        self.selected_algo_id = None
        self.src_img_pil = None
        self.src_img_opencv = None
        self.logger = logger
        self.algo_list = []
        self.algo_selected = {}
        self.algo_data_list = []
        self.selected_algo_list = []
        self.opencv_algo_wrapper = OpencvAlgoWrapper(logger)
        self._build()

        #
        # self.added_algoitem_list = [
        #    {"algo_id": "threshold", "algo_name": "二值化Threshold",
        #     "algo_params": {"thresh": 0.5, "maxval": 0.7}},
        #    {"algo_id": "gaussblur", "algo_name": "高斯滤波GaussBlur",
        #     "algo_params": {"ksize": {"ksize_x": 5, "ksize_y": 5}, "sigmaX": 1}},
        #    {"algo_id": "medianblur", "algo_name": "中值滤波MedianBlur", "algo_params": {"ksize": 11}},
        # ]

    def handle_mouse_iimage_init(self, e: events.MouseEventArguments):
        color = 'SkyBlue' if e.type == 'click' else 'SteelBlue'
        self.src_iimage.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="1" fill="none" stroke="{color}" stroke-width="4" />'
        ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')

    def handle_mouse_ii_processed(self, e: events.MouseEventArguments):
        color = 'SkyBlue' if e.type == 'click' else 'SteelBlue'
        self.dst_img_processed.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="1" fill="none" stroke="{color}" stroke-width="4" />'
        ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')

    def handle_pic_upload(self, e: events.UploadEventArguments):
        self.logger.debug('handle PIC upload')
        src_img_file_data = e.content.read()

        # 使用BytesIO将字节数据转为文件对象
        src_img_bytes = io.BytesIO(src_img_file_data)

        # img_cv2 = cv2.imdecode(np.asarray(bytearray(bytes_data), dtype='uint8'), cv2.IMREAD_COLOR)

        # 使用PIL的Image类从文件对象中读取图像
        self.src_img_pil = Image.open(src_img_bytes)

        # 如果需要转换为OpenCV的图像格式
        self.src_img_opencv = np.array(self.src_img_pil)

        self.logger.debug(f'uploaded file length={len(src_img_file_data)}')

        self.src_iimage.set_source(self.src_img_pil)
        self.src_iimage.update()
        # content.set_content(text)
        # dialog.open()

    def handle_photo_removed(self, e: events.UploadEventArguments):
        self.logger.debug('removed')

    def do_image_processing(self):
        self.logger.debug(f'selected algo list={self.ui_select_algo_list.value}')
        if len(self.ui_select_algo_list.value) < 1:
            self.dst_img_processed.set_source(self.src_img_pil)
        else:
            img = copy.copy(self.src_img_opencv)
            img_pil = Image.fromarray(self.src_img_opencv)
            for algo in list(self.ui_select_algo_list.value):
                if algo == 'openning':
                    img, img_pil = self.opencv_algo_wrapper.opencv_openning(img, (5, 5))
                elif algo == 'grey':
                    img, img_pil = self.opencv_algo_wrapper.opencv_grey(img)
                elif algo == 'gaussblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_guass_blur(img)
                elif algo == 'medianblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_median_blur(img)
                elif algo == 'sobelblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_sobel_blur(img)
                elif algo == 'cannyblur':
                    img, img_pil = self.opencv_algo_wrapper.opencv_canny_blur(img)
                elif algo == 'threshold':
                    img, img_pil = self.opencv_algo_wrapper.opencv_threshold(img, 150, 255)
                elif algo == 'contours':
                    img, img_pil = self.opencv_algo_wrapper.opencv_find_contours(img)
                elif algo == 'erode':
                    img, img_pil = self.opencv_algo_wrapper.opencv_erode(img)
                else:
                    pass

            self.logger.debug(f'the type of processed image={type(img)}')
            self.dst_img_processed.set_source(img_pil)

    def add_row(self):
        new_id = max((dx['id'] for dx in rows), default=-1) + 1
        rows.append({'id': new_id, 'name': 'New name', 'age': None})
        ui.notify(f'Added row with ID {new_id}')
        self.aggrid.update()

    def handle_algo_selected(self, e: ValueChangeEventArguments):
        self.selected_algo_id = e.value

    def add_to_algo_list(self):
        self.logger.debug(self.selected_algo_id)
        algo_id = self.selected_algo_id
        selected_algo = OpencvAlgoWrapper.get_default_algo(algo_id)
        algo_name = selected_algo['algo_id']
        algo_params = selected_algo['algo_params']
        self.algo_data_list.append({"algo_id": algo_id, "algo_name": algo_name, "algo_params": json.dumps(algo_params)})
        self.algo_grid.update()

    def handle_cell_value_change(self, e):
        new_row = e.args['data']
        ui.notify(f'Updated row to: {e.args["data"]}')
        self.algo_data_list[:] = [row | new_row if row['id'] == new_row['id'] else row for row in
                                  self.algo_data_list]

    def handle_row_selected(self, msg: GenericEventArguments):
        selected_row_data = msg.args['data']
        self.logger.debug(f'selected_row_data={selected_row_data}')
        # self.algo_params_ctl.refresh(selected_row_data['algo_params'])

        self.algo_params_manager(selected_row_data['algo_id'])

    async def remove_from_algo_list(self):
        selected_rows = await self.algo_grid.get_selected_rows()
        self.algo_data_list[:] = [row for row in self.algo_data_list if row not in selected_rows]
        self.algo_grid.update()

    async def apply_algo_of_list(self):
        self.selected_algo_list = await self.algo_grid.get_selected_rows()
        self.logger.debug(f'selected algo list={self.selected_algo_list}')
        if len(self.selected_algo_list) < 1:
            self.dst_img_processed.set_source(self.src_img_pil)
            return

        img = copy.copy(self.src_img_opencv)
        img_pil = Image.fromarray(self.src_img_opencv)

        for algo in self.selected_algo_list:
            if algo['algo_id'] == 'openning':
                img, img_pil = self.opencv_algo_wrapper.opencv_openning(img, (5, 5))
            elif algo['algo_id'] == 'grey':
                img, img_pil = self.opencv_algo_wrapper.opencv_grey(img)
            elif algo['algo_id'] == 'gaussblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_guass_blur(img)
            elif algo['algo_id'] == 'medianblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_median_blur(img)
            elif algo['algo_id'] == 'sobelblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_sobel_blur(img)
            elif algo['algo_id'] == 'cannyblur':
                img, img_pil = self.opencv_algo_wrapper.opencv_canny_blur(img)
            elif algo['algo_id'] == 'threshold':
                img, img_pil = self.opencv_algo_wrapper.opencv_threshold(img, 150, 255)
            elif algo['algo_id'] == 'contours':
                img, img_pil = self.opencv_algo_wrapper.opencv_find_contours(img)
            elif algo['algo_id'] == 'erode':
                img, img_pil = self.opencv_algo_wrapper.opencv_erode(img)
            else:
                pass

        self.logger.debug(f'the type of processed image={type(img)}')
        self.dst_img_processed.set_source(img_pil)

    @ui.refreshable
    def algo_params_manager(self, algo_id: str):
        pass

    def _build(self):
        with ui.row().classes('w-full items-end justify-items-end'):
            ui.label('产线定位检测').classes('text-h6 font-bold')
        with ui.row().classes('w-full content-start'):
            with ui.column().classes('w-64 content-start'):
                ui.label('图片上传').classes('font-bold')
                ui.upload(label='产线照片上传', on_upload=self.handle_pic_upload, auto_upload=True).classes('max-w-full')
            with ui.column().classes('W-64 content-start'):
                src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
                ui.label('原始图像').classes('font-bold')
                self.src_iimage = ui.interactive_image(src, on_mouse=self.handle_mouse_iimage_init,
                                                       events=['mousedown', 'mouseup'],
                                                       cross='red').on("removed", self.handle_photo_removed)
        ui.separator()
        with ((ui.row().classes('w-full items-end justify-items-end'))):
            ui.select(options=self.algo_list, multiple=False, label='已选算法列表').props('use-chips').classes(
                'w-64').on_value_change(self.handle_algo_selected)
            ui.button("添加", on_click=self.add_to_algo_list)
            ui.button("删除选中", on_click=self.remove_from_algo_list)
            ui.button('执行列表算法', on_click=self.apply_algo_of_list)
        with ui.row().classes('w-full items-start justify-items-end'):
            with ui.column().classes('w-2/5 h-full items-start') as param_list_col:
                self.algo_grid = ui.aggrid({
                    "columnDefs": [
                        {'headerName': '算法标识', 'field': 'algo_id', 'checkboxSelection': True},
                        {'headerName': '算法名称', 'field': 'algo_name'},
                        {'headerName': '算法参数', 'field': 'algo_params'}
                    ],
                    "rowData": list(map(lambda algo: json.dumps(algo['algo_params']), self.algo_data_list)),
                    "rowSelection": "multiple",
                    "stopEditingWhenCellsLoseFocus": True,
                }).on('cellValueChanged', self.handle_cell_value_change).on('finish', lambda: ui.notify('Finish!'))

                self.algo_grid.on('rowSelected', self.handle_row_selected)

            self.param_detail_col = ui.column().classes('w-1/2 min-h-96 items-start')

            async def get_data() -> None:
                data = await self.json_editor.run_editor_method('get')
                ui.notify(data)
                ui.button('Get Data', on_click=get_data)
        ui.separator()
        with ui.row().classes('w-full items-end justify-items-end'):
            self.ui_select_algo_list = ui.select(options=self.algo_list, multiple=True, label='已选算法列表',
                                                 value=self.algo_data_list).classes('w-full').props('use-chips')
            ui.button('执行选中算法', on_click=self.do_image_processing).props('color=blue')
        with ui.row().classes('w-full items-end justify-items-end'):
            with ui.column().classes('w-full h-full'):
                ui.label('处理后图像')
                self.dst_img_processed = ui.interactive_image(src, on_mouse=self.handle_mouse_ii_processed,
                                                              events=['mousedown', 'mouseup'],
                                                              cross='red')
